package org.ccheng.common;

import cn.hutool.core.lang.PatternPool;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReUtil;
import cn.hutool.core.util.ReflectUtil;
import org.ccheng.common.annotation.Attribute;
import org.ccheng.common.annotation.AttributeId;
import org.ccheng.common.annotation.AttributeList;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.*;
import java.util.regex.Pattern;

public class BeanUtil {

    public static <T> T toBean(String content, Class<T> type) {
        return toBean(content, type, 0);
    }

    public static <T> T toBean(String content, Class<T> type, int index) {
        T t = null;
        try {
            t = type.newInstance();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

        Field[] fields = ReflectUtil.getFields(type);

        String id = null;

        for (Field field : fields) {
            if (field.isAnnotationPresent(Attribute.class)) {

                Attribute attribute = field.getAnnotation(Attribute.class);

                String regex = getRegex(attribute);

                if (regex == null)
                    continue;
                if (id != null)
                    regex = String.format(regex, id);

                List<String> values = ReUtil.findAll(PatternPool.get(regex, Pattern.DOTALL), content, 0);

                String value = null;
                if (values.size() > index) {
                    value = values.get(index);

                    while ("".equals(value) && values.size() < index) {
                        value = values.get(index++);
                    }
                }

                ReflectUtil.setFieldValue(t, field, value);

                if (field.isAnnotationPresent(AttributeId.class)) {
                    id = value;
                }
            } else if (field.isAnnotationPresent(AttributeList.class)) {
                if (field.getType() == List.class) {
                    Type genericType = field.getGenericType();
                    ParameterizedType parameterizedType = (ParameterizedType) genericType;
                    Type[] genericTypes = parameterizedType.getActualTypeArguments();

                    if (genericTypes.length != 1)
                        continue;

                    Class<?> fieldClass = (Class<?>) genericTypes[0];

                    Field idField = findField(fieldClass, AttributeId.class);

                    if (idField == null)
                        continue;

                    Attribute attribute = idField.getAnnotation(Attribute.class);

                    String regex = getRegex(attribute);

                    if (regex == null)
                        continue;
                    if (id != null)
                        regex = String.format(regex, id);

                    List<String> values = ReUtil.findAll(PatternPool.get(regex), content, 0);

                    List list = new ArrayList();

                    for (int i = 0; i < values.size(); i++) {
                        Object o = null;
                        try {
                            o = toBean(content, Class.forName(genericTypes[0].getTypeName()), i);
                            list.add(o);
                        } catch (ClassNotFoundException e) {
                            throw new RuntimeException(e);
                        }
                    }

                    ReflectUtil.setFieldValue(t, field, list);
                }
            }
        }

        return t;
    }

    public static String getRegex(Attribute attribute) {
        String regex = attribute.type().getValue();

        if (ObjectUtil.isEmpty(regex))
            regex = attribute.regex();

        if (ObjectUtil.isEmpty(regex))
            return null;

        if (attribute.format())
            regex = String.format(regex, attribute.key());

        return regex;
    }

    public static Field findField(Class type, Class<? extends Annotation> annotation) {
        Field[] fields = ReflectUtil.getFields(type);
        return findField(fields, annotation);
    }

    public static Field findField(Field[] fields, Class<? extends Annotation> annotation) {
        for (Field field : fields) {
            if (field.isAnnotationPresent(annotation))
                return field;
        }
        return null;
    }

    public static List<Map<String, String>> getAttribute(Object o, Class type) {
        Field[] fields = ReflectUtil.getFields(type);

        List<Map<String, String>> list = new ArrayList<>();

        for (Field field : fields) {
            if (!field.isAnnotationPresent(Attribute.class))
                continue;

            Attribute attribute = field.getAnnotation(Attribute.class);

            if (!attribute.view())
                continue;

            String value = (String) ReflectUtil.getFieldValue(o, field);

            Map<String, String> map = new HashMap<>();
            map.put("key", attribute.key());
            map.put("value", value);

            list.add(map);
        }

        return list;
    }
}
